import logging
from typing import Tuple
from m600_communicator import M600Communicator

class PSAMCardHandler:
    """
    PSAM卡操作处理器
    封装PSAM卡相关操作，包括文件读写、MAC计算等功能
    """
    
    def __init__(self, communicator: M600Communicator):
        """
        初始化PSAM卡处理器
        :param communicator: M600通信器实例
        """
        self.comm = communicator
        self.logger = logging.getLogger(__name__)
    
    def write_card_info(self, f0015_data: bytes, f0016_data: bytes = None) -> Tuple[bool, str]:
        """
        写入IC卡基本信息(CPU卡二次发行)
        :param f0015_data: 0015文件数据(必填)
        :param f0016_data: 0016文件数据(可选)
        :return: (操作是否成功, 错误信息)
        """
        try:
            self.logger.info("========== 写IC卡基本信息，CPU卡二次发行 ===========")
            
            # PSAM卡初始化
            if not self._psam_init():
                return False, "PSAM卡初始化失败"
                
            # 打开卡片并验证是否为CPU卡
            card_id, err = self._open_and_verify_card()
            if err:
                return False, err
                
            # 读取现有文件数据
            if not self._read_existing_files():
                return False, "读取现有文件失败"
                
            # 写入0016文件(如果提供了数据)
            if f0016_data and not self._write_file_16(f0016_data):
                return False, "写入0016文件失败"
                
            # 写入0015文件
            if not self._write_file_15(f0015_data):
                return False, "写入0015文件失败"
                
            return True, ""
            
        except Exception as e:
            self.logger.error(f"写入IC卡信息时发生异常: {str(e)}")
            return False, str(e)
    
    def _psam_init(self) -> bool:
        """
        PSAM卡初始化
        :return: 是否初始化成功
        """
        try:
            # 进入DF01目录
            cmd = "00A4000002DF01"
            response = self.TransferChannel(Apdus = cmd)
            if not response:
                self.logger.error("PSAM进入DF01目录失败")
                return False
                
            self.logger.info("PSAM卡初始化成功")
            return True
            
        except Exception as e:
            self.logger.error(f"PSAM卡初始化异常: {str(e)}")
            return False
    
    def _get_card_type(self) -> int:
        """
        获取卡片类型
        :return: 卡片类型标识 (2表示CPU卡)
        """
        try:
            # 发送获取卡片类型命令
            cmd = "00A40000023F00"
            response = self.TransferChannel(Apdus = cmd)
            
            # 解析响应判断卡片类型
            if response and len(response) >= 2:
                # 假设响应最后2字节表示卡片类型
                return int(response[-2:], 16)
            return 0
            
        except Exception as e:
            self.logger.error(f"获取卡片类型异常: {str(e)}")
            return 0
            
    def _open_and_verify_card(self) -> Tuple[str, str]:
        """
        打开卡片并验证是否为CPU卡
        :return: (卡片ID, 错误信息)
        """
        try:
            # 打开卡片
            cmd = "00A40000023F00"
            response = self.TransferChannel( Apdus = cmd)
                
            # 验证卡片类型
            card_type = self._get_card_type()
            if card_type != 2:
                return "", "当前卡片不是CPU卡"
                
            # 获取卡片ID
            card_id = response.hex()[-8:]  # 假设ID在响应最后4字节
            self.logger.info(f"成功打开CPU卡, ID: {card_id}")
            return card_id, ""
            
        except Exception as e:
            return "", f"打开卡片异常: {str(e)}"
    
    def _read_existing_files(self) -> bool:
        """
        读取现有文件数据(0015和0016文件)
        :return: 是否读取成功
        """
        try:
            # 读取0016文件(55字节)
            cmd = "00B0960037"
            response = self.TransferChannel( Apdus = cmd)
            if not success:
                self.logger.error("读取0016文件失败")
                return False
                
            # 读取0015文件(43字节)
            cmd = "00B095002B"
            response = self.TransferChannel( Apdus = cmd)
            if not success:
                self.logger.error("读取0015文件失败")
                return False
                
            self.logger.info("成功读取现有文件数据")
            return True
            
        except Exception as e:
            self.logger.error(f"读取文件异常: {str(e)}")
            return False
    
    def _write_file_16(self, data: bytes, card_id: str = "", issuer_code: str = "", key_id: str = "2643", need_mac: bool = True) -> bool:
        """
        写入0016文件(2,53)
        :param data: 要写入的数据(53字节)
        :return: 是否写入成功
        """
        try:
            # 进入3F00目录
            cmd = "00A40000023F00"
            response = self.TransferChannel( Apdus = cmd)
            if not success:
                self.logger.error("进入3F00目录失败")
                return False
                
            # 获取随机数
            cmd = "0084000004"
            rand_num = self.TransferChannel( Apdus = cmd)
            if not success:
                self.logger.error("获取随机数失败")
                return False
                
            # 生成写指令
            write_cmd = self._make_write_instruction(data, 0x16, 2, True)
            
            # 计算MAC
            mac = self._calculate_mac(write_cmd, rand_num)
            
            # 发送写指令
            full_cmd = write_cmd + mac
            response = self.TransferChannel( Apdus = cmd)
            
            if success:
                self.logger.info("成功写入0016文件")
                return True
            else:
                self.logger.error("写入0016文件失败")
                return False
                
        except Exception as e:
            self.logger.error(f"写入0016文件异常: {str(e)}")
            return False
    
    def _write_file_15(self, data: bytes) -> bool:
        """
        写入0015文件(20,23)
        :param data: 要写入的数据(23字节)
        :return: 是否写入成功
        """
        try:
            # 进入1001目录
            cmd = "00A40000021001"
            success, response = self.TransferChannel( Apdus = cmd)
            if not success:
                self.logger.error("进入1001目录失败")
                return False
                
            # 获取随机数
            cmd = "0084000004"
            rand_num = self.TransferChannel( Apdus = cmd)
            if not success:
                self.logger.error("获取随机数失败")
                return False
                
            # 生成写指令
            write_cmd = self._make_write_instruction(data, 0x15, 20, True)
            
            # 计算MAC
            mac = self._calculate_mac(write_cmd, rand_num)
            
            # 发送写指令
            full_cmd = write_cmd + mac
            response = self.TransferChannel( Apdus = cmd)
            
            if success:
                self.logger.info("成功写入0015文件")
                return True
            else:
                self.logger.error("写入0015文件失败")
                return False
                
        except Exception as e:
            self.logger.error(f"写入0015文件异常: {str(e)}")
            return False
    
    def _make_write_instruction(self, data: bytes, file_id: int, start_pos: int, need_mac: bool) -> bytes:
        """
        生成写文件指令
        :param data: 要写入的数据
        :param file_id: 文件ID
        :param start_pos: 起始位置
        :param need_mac: 是否需要MAC
        :return: 指令字节
        """
        try:
            # 04D6 + (80|XX) + start + (len+4) + hexdata
            cmd = bytearray()
            cmd.extend(bytes.fromhex("04D6"))
            cmd.append(0x80 if need_mac else 0x00)
            cmd.append(start_pos)
            cmd.append(len(data) + 4)  # 数据长度+4字节MAC
            cmd.extend(data)
            
            self.logger.debug(f"生成的写指令: {cmd.hex()}")
            return bytes(cmd)
            
        except Exception as e:
            self.logger.error(f"生成写指令异常: {str(e)}")
            raise
    
    def _calculate_mac(self, data: bytes, rand_num: bytes) -> bytes:
        """
        计算MAC值
        :param data: 要计算的数据
        :param rand_num: 随机数
        :return: 4字节MAC值
        """
        try:
            # 组装MAC计算数据: 随机数+写指令+补80和8的倍数
            mac_data = bytearray()
            mac_data.extend(rand_num)
            mac_data.extend(data)
            
            # 补80和填充到8的倍数
            mac_data.append(0x80)
            while len(mac_data) % 8 != 0:
                mac_data.append(0x00)
                
            # 构造APDU指令
            cmd = bytearray.fromhex("80FA0500")
            cmd.append(len(mac_data))  # 数据长度
            cmd.extend(mac_data)
            
            # 发送到PSAM计算MAC
            response = self.TransferChannel(ApduList="01", Apdus=cmd.hex().upper())
            if response and len(response) >= 8:  # 确保响应包含MAC和状态码
                if response[-4:] == "9000":  # 检查状态码是否为成功
                    return bytes.fromhex(response[:8])  # 返回前8字节作为MAC
                else:
                    raise Exception(f"PSAM计算MAC失败, 状态码: {response[-4:]}")
            else:
                raise Exception("PSAM计算MAC失败, 无效响应")
                
        except Exception as e:
            self.logger.error(f"计算MAC异常: {str(e)}")
            raise
    
    def TransferChannel(self, ApduList: str, Apdus: str) -> str:
        """
        透传通道操作
        :param ApduList: APDU列表
        :param Apdus: APDU指令数据
        :return: 响应数据(十六进制字符串)
        """
        return self.comm.TransferChannel(ApduList=ApduList, Apdus=Apdus)
        
    def _send_sam_command(self, cmd: bytes) -> Tuple[bool, bytes]:
        """
        发送SAM命令并获取响应
        :param cmd: 命令字节
        :return: (是否成功, 响应数据)
        """
        try:
            # 使用M600通信器发送命令
            hex_str = cmd.hex(" ").upper()
            response = self.TransferChannel( Apdus = cmd)
            
            if response:
                # 验证响应状态是否为9000(成功)
                if response[-4:] == "9000":
                    return True, bytes.fromhex(response[:-4])
                else:
                    self.logger.error(f"SAM命令失败, 状态码: {response[-4:]}")
                    return False, bytes.fromhex(response[:-4])
            else:
                return False, b""
                
        except Exception as e:
            self.logger.error(f"发送SAM命令异常: {str(e)}")
            return False, b""
    
    def _send_pro_command(self, cmd: bytes) -> Tuple[bool, bytes]:
        """
        发送PRO命令并获取响应
        :param cmd: 命令字节
        :return: (是否成功, 响应数据)
        """
        try:
            # 使用M600通信器发送命令
            hex_str = cmd.hex(" ").upper()
            response = self.comm.TransferChannel( Apdus = cmd)
            
            if response:
                # 验证响应状态是否为9000(成功)
                if response[-4:] == "9000":
                    return True, bytes.fromhex(response[:-4])
                else:
                    self.logger.error(f"PRO命令失败, 状态码: {response[-4:]}")
                    return False, bytes.fromhex(response[:-4])
            else:
                return False, b""
                
        except Exception as e:
            self.logger.error(f"发送PRO命令异常: {str(e)}")
            return False, b""

    def Into_IC_3F00(self):
        """
        进入IC卡的3F00目录
        :return: 是否成功
        """
        try:
            # 进入IC卡的3F00目录
            cmd = "00A40000023F00"
            response = self.TransferChannel( Apdus = cmd)
            if not response:
                self.logger.error("进入IC卡3FOO目录失败")
                return False
                
            self.logger.info("进入IC卡3FOO目录成功")
            return True
            
        except Exception as e:
            self.logger.error(f"进入IC卡3FOO目录异常: {str(e)}")
            return False
    def Get_PSAM_Card_ID(self):
        """
        获取PSAM卡ID
        :return: 卡片ID(十六进制字符串)
        """
        try:
            # 已经进入3F00目录，直接读取卡片ID
            cmd = "00B0960006"  # 读取卡片ID命令
            response = self.TransferChannel( Apdus = cmd)
            if not response:
                self.logger.error("读取卡片ID失败")
                return ""

            # 假设卡片ID在响应的最后4字节
            card_id = response[-8:]
            self.logger.info(f"获取的卡片ID: {card_id}")
            return card_id
        except Exception as e:
            self.logger.error(f"获取卡片ID异常: {str(e)}")
            return ""

    def Into_PSAM_DF01(self):
        """
        进入PSAM卡的DF01目录
        :return: 是否成功
        """
        try:
            # 进入PSAM卡的DF01文件
            cmd = "00A4000002DF01"
            response = self.TransferChannel( Apdus = cmd)
            if not response:
                self.logger.error("进入PSAM卡DF01目录失败")
                return False
            self.logger.info("进入PSAM卡DF01目录成功")
            return True
            
        except Exception as e:
            self.logger.error(f"进入PSAM卡DFO1目录异常: {str(e)}")
            return False


def main():
    """
    PSAM卡处理器调试主函数
    测试核心功能包括文件读写和MAC计算
    """
    import logging
    from m600_communicator import M600Communicator
    
    # 配置日志
    logging.basicConfig(level=logging.INFO)
    logger = logging.getLogger(__name__)
    
    try:
        # 创建通信器实例
        comm = M600Communicator()
        comm.InitRSU()
        comm.SendBST()
        # 创建PSAM卡处理器实例
        handler = PSAMCardHandler(comm)
        logger.info("PSAM卡处理器初始化完成")
        handler.Into_IC_3F00()
        
        handler.Into_PSAM_DF01()
        handler.Get_PSAM_Card_ID()
        # # 测试PSAM卡初始化
        # if handler._psam_init():
        #     logger.info("PSAM卡初始化测试通过")
        # else:
        #     logger.error("PSAM卡初始化测试失败")
        #     return
        
        # # 测试打开并验证卡片
        # card_id, err = handler._open_and_verify_card()
        # if not err:
        #     logger.info(f"成功打开CPU卡, ID: {card_id}")
        # else:
        #     logger.error(f"打开卡片失败: {err}")
        #     return
        
        # # 测试读取现有文件
        # if handler._read_existing_files():
        #     logger.info("读取现有文件测试通过")
        # else:
        #     logger.error("读取现有文件测试失败")
            
        # # 测试写入0015文件(示例数据)
        # test_data_15 = bytes([0x01]*23)  # 23字节测试数据
        # if handler._write_file_15(test_data_15):
        #     logger.info("写入0015文件测试通过")
        # else:
        #     logger.error("写入0015文件测试失败")
        
        # # 测试写入0016文件(示例数据)
        # test_data_16 = bytes([0x02]*53)  # 53字节测试数据
        # if handler._write_file_16(test_data_16):
        #     logger.info("写入0016文件测试通过")
        # else:
        #     logger.error("写入0016文件测试失败")
        
    except Exception as e:
        logger.error(f"调试过程中发生异常: {str(e)}")


if __name__ == "__main__":
    main()